home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / hplip / hpssd.py < prev    next >
Text File  |  2008-10-13  |  16KB  |  499 lines

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # (c) Copyright 2003-2008 Hewlett-Packard Development Company, L.P.
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19. #
  20. # Author: Don Welch
  21. #
  22.  
  23. __version__ = '10.1'
  24. __title__ = "Services and Status System Tray dBus Child Process"
  25. __doc__ = "Provides persistent data and event services to HPLIP client applications. Required to be running for PC send fax, optional in all other cases."
  26.  
  27.  
  28. # StdLib
  29. import sys
  30. import struct
  31. import os
  32. import time
  33. import getopt
  34. import select
  35. import signal
  36. import tempfile
  37.  
  38. # Local
  39. from base.g import *
  40. from base.codes import *
  41. from base import utils, device, status, models
  42.  
  43. # dBus
  44. try:
  45.     from dbus import lowlevel, SystemBus, SessionBus
  46.     import dbus.service
  47.     from dbus.mainloop.glib import DBusGMainLoop
  48.     from gobject import MainLoop
  49.     dbus_loaded = True
  50. except ImportError:
  51.     log.error("dbus failed to load (python-dbus ver. 0.80+ required). Exiting...")
  52.     dbus_loaded = False
  53.     sys.exit(1)
  54.  
  55.  
  56. # Globals
  57. PIPE_BUF = 4096
  58. dbus_loop = None
  59. system_bus = None
  60. session_bus = None
  61. w = None
  62. devices = {} # { 'device_uri' : DeviceCache, ... }
  63.  
  64.  
  65. USAGE = [(__doc__, "", "name", True),
  66.          ("Usage: hpssd.py [OPTIONS]", "", "summary", True),
  67.          utils.USAGE_OPTIONS,
  68.          utils.USAGE_LOGGING1, utils.USAGE_LOGGING2,
  69.          ("Run in debug mode:", "-g (same as options: -ldebug -x)", "option", False),
  70.          utils.USAGE_HELP,
  71.         ]
  72.  
  73.  
  74. def usage(typ='text'):
  75.     if typ == 'text':
  76.         utils.log_title(__title__, __version__)
  77.  
  78.     utils.format_text(USAGE, typ, __title__, 'hpssd.py', __version__)
  79.     sys.exit(0)
  80.  
  81.  
  82.  
  83. class DeviceCache(object):
  84.     def __init__(self, model=''):
  85.         self.history = utils.RingBuffer(prop.history_size) # circular buffer of ServiceEvent
  86.         self.model = models.normalizeModelName(model)
  87.         self.cache = {} # variable name : value
  88.         self.faxes = {} # (username, jobid): FaxEvent
  89.  
  90.  
  91.  
  92. class ServiceEvent(device.Event):
  93.     def __init__(self, device_uri, printer_name, event_code, username, job_id, title):
  94.         device.Event.__init__(self, device_uri, printer_name, event_code, username, job_id, title, time.time())
  95.  
  96.     def debug(self):
  97.         log.debug("EVENT:")
  98.         device.Event.debug(self)
  99.  
  100.     def __str__(self):
  101.         return "<ServiceEvent('%s', '%s', %d, '%s', %d, '%s', %f)>" % self.as_tuple()
  102.  
  103.  
  104.  
  105. class FaxEvent(device.Event):
  106.     def __init__(self, temp_file, event):
  107.         device.Event.__init__(self, *event.as_tuple())
  108.         self.temp_file = temp_file
  109.  
  110.     def debug(self):
  111.         log.debug("FAX:")
  112.         device.Event.debug(self)
  113.         log.debug("    temp_file=%s" % self.temp_file)
  114.  
  115.     def __str__(self):
  116.         return "<FaxEvent('%s', '%s', %d, '%s', %d, '%s', %f, '%s')>" % self.as_tuple()      
  117.  
  118.     def as_tuple(self):
  119.         return (self.device_uri, self.printer_name, self.event_code, 
  120.              self.username, self.job_id, self.title, self.timedate,
  121.              self.temp_file)
  122.  
  123.  
  124.  
  125. #  dbus interface on session bus
  126. class StatusService(dbus.service.Object):
  127.     def __init__(self, name, object_path):
  128.         dbus.service.Object.__init__(self, name, object_path)
  129.  
  130.  
  131.     @dbus.service.method('com.hplip.StatusService', in_signature='s', out_signature='a(ssisisd)')
  132.     def GetHistory(self, device_uri):
  133.         log.debug("GetHistory('%s')" % device_uri)
  134.         try:
  135.             devices[device_uri]
  136.         except KeyError:
  137.             #log.warn("Unknown device URI: %s" % device_uri)
  138.             return []
  139.         else:
  140.             h = devices[device_uri].history.get()
  141.             log.debug("%d events in history:" % len(h))
  142.             [x.debug() for x in h]
  143.             return [x.as_tuple() for x in h]
  144.  
  145.  
  146.  
  147.     @dbus.service.method('com.hplip.StatusService', in_signature='ssi', out_signature='i')
  148.     def SetCachedIntValue(self, device_uri, key, value):
  149.         log.debug("SetCachedIntValue('%s', '%s', %d)" % (device_uri, key, value))
  150.         if check_device(device_uri) == ERROR_SUCCESS: 
  151.             devices[device_uri].cache[key] = value
  152.             return value
  153.  
  154.         return -1
  155.  
  156.  
  157.     @dbus.service.method('com.hplip.StatusService', in_signature='ss', out_signature='i')
  158.     def GetCachedIntValue(self, device_uri, key):
  159.         try:
  160.             ret = devices[device_uri].cache[key]
  161.         except KeyError:
  162.             ret = -1
  163.  
  164.         log.debug("GetCachedIntValue('%s', '%s') --> %d" % (device_uri, key, ret))
  165.         return ret
  166.  
  167.  
  168.     @dbus.service.method('com.hplip.StatusService', in_signature='sss', out_signature='s')
  169.     def SetCachedStrValue(self, device_uri, key, value):
  170.         log.debug("SetCachedStrValue('%s', '%s', '%s')" % (device_uri, key, value))
  171.         if check_device(device_uri) == ERROR_SUCCESS: 
  172.             devices[device_uri].cache[key] = value
  173.             return value
  174.  
  175.         return ''
  176.  
  177.  
  178.     @dbus.service.method('com.hplip.StatusService', in_signature='ss', out_signature='s')
  179.     def GetCachedStrValue(self, device_uri, key):
  180.         try:
  181.             ret = devices[device_uri].cache[key]
  182.         except KeyError:
  183.             ret = ''
  184.  
  185.         log.debug("GetCachedStrValue('%s', '%s') --> %s" % (device_uri, key, ret))
  186.         return ret
  187.  
  188.  
  189.     # Pass a non-zero job_id to retrieve a specific fax
  190.     # Pass zero for job_id to retrieve any avail. fax
  191.     @dbus.service.method('com.hplip.StatusService', in_signature='ssi', out_signature='ssisisds')
  192.     def CheckForWaitingFax(self, device_uri, username, job_id=0):
  193.         #device_uri = device_uri.replace('hp:', 'hpfax:')
  194.         log.debug("CheckForWaitingFax('%s', '%s', %d)" % (device_uri, username, job_id))
  195.         r = (device_uri, '', 0, username, job_id, '', 0.0, '')
  196.         
  197.         check_device(device_uri)
  198.         #try:
  199.         #    devices[device_uri]
  200.         #except KeyError:
  201.         #    log.warn("Unknown device URI: %s" % device_uri)
  202.         #    return r
  203.         #else:
  204.         if 1:
  205.             show_waiting_faxes(device_uri)
  206.  
  207.             if job_id: # check for specific job_id
  208.                 try:
  209.                     devices[device_uri].faxes[(username, job_id)]
  210.                 except KeyError:
  211.                     return r
  212.                 else:
  213.                     return self.check_for_waiting_fax_return(device_uri, username, job_id)
  214.  
  215.             else: # return any matching one from cache. call mult. times to get all.
  216.                 for u, j in devices[device_uri].faxes.keys():
  217.                     if u == username:
  218.                         return self.check_for_waiting_fax_return(device_uri, u, j)
  219.  
  220.                 return r
  221.  
  222.  
  223.     # if CheckForWaitingFax returns a fax job, that job is removed from the cache
  224.     def check_for_waiting_fax_return(self, d, u, j):
  225.         log.debug("Fax (username=%s, jobid=%d) removed from faxes and returned to caller." % (u, j))
  226.         r = devices[d].faxes[(u, j)].as_tuple()
  227.         del devices[d].faxes[(u, j)]
  228.         show_waiting_faxes(d)
  229.         return r 
  230.  
  231.  
  232.     # Alternate way to "send" an event rather than using a signal message
  233.     @dbus.service.method('com.hplip.StatusService', in_signature='ssisis', out_signature='')
  234.     def SendEvent(self, device_uri, printer_name, event_code, username, job_id, title):
  235.         event = ServiceEvent(device_uri, printer_name, event_code, username, job_id, title)
  236.         handle_event(event)
  237.  
  238.  
  239.  
  240. def check_device(device_uri):
  241.     try:
  242.         devices[device_uri]
  243.     except KeyError:
  244.         log.debug("New device: %s" % device_uri)
  245.         try:
  246.             back_end, is_hp, bus, model, serial, dev_file, host, port = \
  247.                 device.parseDeviceURI(device_uri)
  248.         except Error:
  249.             log.error("Invalid device URI")
  250.             return ERROR_INVALID_DEVICE_URI
  251.  
  252.         devices[device_uri] = DeviceCache(model)
  253.  
  254.     return ERROR_SUCCESS   
  255.  
  256.  
  257. def create_history(event):
  258.     history = devices[event.device_uri].history.get()
  259.  
  260.     if history and history[-1].event_code == event.event_code:
  261.         log.debug("Duplicate event. Replacing previous event.")
  262.         devices[event.device_uri].history.replace(event)
  263.         return True
  264.     else:
  265.         devices[event.device_uri].history.append(event)
  266.         return False
  267.  
  268.  
  269. def handle_fax_event(event, pipe_name):
  270.     if event.event_code == EVENT_FAX_RENDER_COMPLETE and \
  271.         event.username == prop.username:
  272.  
  273.         fax_file_fd, fax_file_name = tempfile.mkstemp(prefix="hpfax-")
  274.         pipe = os.open(pipe_name, os.O_RDONLY) 
  275.         bytes_read = 0
  276.         while True:
  277.             data = os.read(pipe, PIPE_BUF)
  278.             if not data: 
  279.                 break
  280.  
  281.             os.write(fax_file_fd, data)
  282.             bytes_read += len(data)
  283.  
  284.         log.debug("Saved %d bytes to file %s" % (bytes_read, fax_file_name))
  285.  
  286.         os.close(pipe)
  287.         os.close(fax_file_fd)
  288.  
  289.         devices[event.device_uri].faxes[(event.username, event.job_id)] = \
  290.             FaxEvent(fax_file_name, event)
  291.  
  292.         show_waiting_faxes(event.device_uri)
  293.  
  294.         try:
  295.             os.waitpid(-1, os.WNOHANG)
  296.         except OSError:
  297.             pass
  298.  
  299.         # See if hp-sendfax is already running for this queue
  300.         ok, lock_file = utils.lock_app('hp-sendfax-%s' % event.printer_name, True)
  301.  
  302.         if ok: 
  303.             # able to lock, not running...
  304.             utils.unlock(lock_file)
  305.             
  306.             path = utils.which('hp-sendfax')
  307.             if path:
  308.                 path = os.path.join(path, 'hp-sendfax')
  309.             else:
  310.                 log.error("Unable to find hp-sendfax on PATH.")
  311.                 return
  312.  
  313.             log.debug(path)
  314.             
  315.             log.debug("Running hp-sendfax: hp-senfax --fax=%s" % event.printer_name)
  316.             
  317.             os.spawnlp(os.P_NOWAIT, path, 'hp-sendfax', 
  318.                 '--fax=%s' % event.printer_name)
  319.         
  320.         else: 
  321.             # hp-sendfax running
  322.             # no need to do anything... hp-sendfax is polling
  323.             log.debug("hp-sendfax is running. Waiting for CheckForWaitingFax() call.")
  324.  
  325.     else:
  326.         log.warn("Not handled!")
  327.         pass
  328.  
  329.  
  330. def show_waiting_faxes(d):
  331.     f = devices[d].faxes
  332.     if not len(f):
  333.         log.debug("No faxes waiting for %s" % d)
  334.     else:
  335.         if len(f) == 1:
  336.             log.debug("1 fax waiting for %s:" % d)
  337.         else:
  338.             log.debug("%d faxes waiting for %s:" % (len(f), d))
  339.  
  340.         [f[x].debug() for x in f]
  341.  
  342.  
  343. def handle_event(event, more_args=None):
  344.     log.debug("Handling event...")
  345.     if more_args is None: 
  346.         more_args = []
  347.  
  348.     event.debug()
  349.  
  350.     if event.device_uri and check_device(event.device_uri) != ERROR_SUCCESS:
  351.         return
  352.    
  353.     # If event-code > 10001, its a PJL error code, so convert it
  354.     if event.event_code > EVENT_MAX_EVENT:
  355.         event.event_code = status.MapPJLErrorCode(event.event_code)
  356.  
  357.     # regular user/device status event
  358.     if EVENT_MIN_USER_EVENT <= event.event_code <= EVENT_MAX_USER_EVENT:
  359.         
  360.         if event.device_uri:
  361.             #event.device_uri = event.device_uri.replace('hpfax:', 'hp:')
  362.             dup_event = create_history(event)
  363.  
  364.         # Send to system tray icon if available
  365.         if not dup_event and event.event_code != STATUS_PRINTER_IDLE:
  366.             if w is not None:
  367.                 log.debug("Sending event to system tray icon UI...")
  368.                 try:
  369.                     os.write(w, event.pack())
  370.                 except OSError:
  371.                     log.debug("Failed.")
  372.                     
  373.         # send EVENT_HISTORY_UPDATE signal to hp-toolbox
  374.         send_toolbox_event(event, EVENT_HISTORY_UPDATE)
  375.  
  376.         
  377.     # Handle fax signals
  378.     elif EVENT_FAX_MIN <= event.event_code <= EVENT_FAX_MAX and more_args:
  379.         log.debug("Fax event")
  380.         pipe_name = str(more_args[0])
  381.         handle_fax_event(event, pipe_name)
  382.  
  383.  
  384. def send_toolbox_event(event, event_code):
  385.     args = [event.device_uri, event.printer_name, event_code, 
  386.             prop.username, event.job_id, event.title, '']
  387.             
  388.     msg = lowlevel.SignalMessage('/', 'com.hplip.Toolbox', 'Event')
  389.     msg.append(signature='ssisiss', *args)
  390.  
  391.     SessionBus().send_message(msg)
  392.  
  393.  
  394. def handle_signal(typ, *args, **kwds):
  395.     if kwds['interface'] == 'com.hplip.Service' and \
  396.         kwds['member'] == 'Event':
  397.  
  398.         event = ServiceEvent(*args[:6])
  399.         return handle_event(event, args[6:])
  400.  
  401.  
  402. def handle_system_signal(*args,**kwds):
  403.     return handle_signal('system', *args, **kwds)
  404.  
  405.  
  406. def handle_session_signal(*args, **kwds):
  407.     return handle_signal('session', *args, **kwds)
  408.  
  409.  
  410. # Entry point for hp-systray
  411. def run(write_pipe=None, parent_pid=0):
  412.     global dbus_loop
  413.     global system_bus
  414.     global session_bus
  415.     global w
  416.  
  417.     log.set_module("hp-systray(hpssd)")
  418.     w = write_pipe
  419.  
  420.     dbus_loop = DBusGMainLoop(set_as_default=True)
  421.     
  422.     try:
  423.         system_bus = SystemBus(mainloop=dbus_loop)
  424.     except dbus.exceptions.DBusException, e:        
  425.         log.error("Unable to connect to dbus system bus. Exiting.")
  426.         sys.exit(1)
  427.  
  428.     try:
  429.         session_bus = dbus.SessionBus()
  430.     except dbus.exceptions.DBusException, e:
  431.         if os.getuid() != 0:
  432.             log.error("Unable to connect to dbus session bus. Exiting.")
  433.             sys.exit(1)
  434.         else:
  435.             log.error("Unable to connect to dbus session bus (running as root?)")            
  436.             sys.exit(1)    
  437.  
  438.     # Receive events from the system bus
  439.     system_bus.add_signal_receiver(handle_system_signal, sender_keyword='sender',
  440.         destination_keyword='dest', interface_keyword='interface',
  441.         member_keyword='member', path_keyword='path')
  442.  
  443.     # Receive events from the session bus
  444.     session_bus.add_signal_receiver(handle_session_signal, sender_keyword='sender',
  445.         destination_keyword='dest', interface_keyword='interface',
  446.         member_keyword='member', path_keyword='path')
  447.  
  448.     # Export an object on the session bus
  449.     session_name = dbus.service.BusName("com.hplip.StatusService", session_bus)
  450.     status_service = StatusService(session_name, "/com/hplip/StatusService")
  451.  
  452.     log.debug("Entering main loop...")
  453.     try:
  454.         MainLoop().run()
  455.     except KeyboardInterrupt:
  456.         log.debug("Ctrl-C: Exiting...")
  457.  
  458.  
  459.  
  460. if __name__ == '__main__':
  461.     log.set_module('hpssd')
  462.  
  463.     try:
  464.         opts, args = getopt.getopt(sys.argv[1:], 'l:hg', 
  465.             ['level=', 'help', 'help-man', 'help-rest', 'help-desc'])
  466.  
  467.     except getopt.GetoptError, e:
  468.         log.error(e.msg)
  469.         usage()
  470.  
  471.     if os.getenv("HPLIP_DEBUG"):
  472.         log.set_level('debug')
  473.  
  474.     for o, a in opts:
  475.         if o in ('-l', '--logging'):
  476.             log_level = a.lower().strip()
  477.             if not log.set_level(log_level):
  478.                 usage()
  479.  
  480.         elif o == '-g':
  481.             log.set_level('debug')
  482.  
  483.         elif o in ('-h', '--help'):
  484.             usage()
  485.  
  486.         elif o == '--help-rest':
  487.             usage('rest')
  488.  
  489.         elif o == '--help-man':
  490.             usage('man')
  491.  
  492.         elif o == '--help-desc':
  493.             print __doc__,
  494.             sys.exit(0)
  495.  
  496.  
  497.     utils.log_title(__title__, __version__)    
  498.     sys.exit(run())
  499.